﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Threading;
using System.Windows.Navigation;
using System.Windows.Threading;
using System.Diagnostics;
using System.Globalization;
using System.Diagnostics.Contracts;

namespace UnitTestUtils.UI
{
    /// <summary>
    /// Helps hosting <see cref="Control"/>s or <see cref="Window"/>s for testing purposes.
    /// </summary>
    public class UIHost
    {
        private Window _wnd;

        private UIHost(Control control)
        {
            Contract.Requires(control != null);
            this.Control = control;
        }

        private UIHost(Page page)
        {
            Contract.Requires(page != null);
            this.Page = page;
        }

        /// <summary>
        /// Hosted control.
        /// </summary>
        public Control Control { get; private set; }

        /// <summary>
        /// Hosted <see cref="Page"/>.
        /// </summary>
        /// <remarks>
        /// There can only be either a Control or a Page set.
        /// </remarks>
        public Page Page { get; set; }

        /// <summary>
        /// Returns the created top-level window.
        /// </summary>
        public Window Window
        {
            get
            {
                return _wnd;
            }
        }

        /// <summary>
        /// Create a UIHost for the passed <see cref="Control"/> or <see cref="Window"/>.
        /// </summary>
        /// <param name="control">Control or Window to host.</param>
        /// <returns></returns>
        public static UIHost Create(Control control)
        {
            Contract.Requires<ArgumentNullException>(control != null, "control");
            return new UIHost(control);
        }

        /// <summary>
        /// Create aUIHost for the passed <see cref="Page"/>. A <see cref="NavigationWindow"/> will be 
        /// created as host underneath.
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static UIHost Create(Page control)
        {
            Contract.Requires<ArgumentNullException>(control != null, "control");
            return new UIHost(control);
        }

        /// <summary>
        /// Shows the hosted UI on a non-modal window.
        /// </summary>
        public void Show()
        {
            checkAndCreateWindow();
            _wnd.Show();
        }

        /// <summary>
        /// Shows the hosted UI on a modal window.
        /// </summary>
        public void ShowDialog()
        {
            checkAndCreateWindow();
            _wnd.ShowDialog();
        }

        /// <summary>
        /// Callback that fires when the window host has loaded.
        /// </summary>
        public Action WindowLoaded { get; set; }

        /// <summary>
        /// Waits until the hosted <see cref="Window"/> is either closed or the default timeout is expired.
        /// </summary>
        /// <returns>true, if window was closed within expected time, false if operation has timed out.</returns>
        public bool WaitForClose()
        {
            if (_wnd == null)
                throw new Exception("Window was never opened!");

            bool wndClosed = false;
            //int cycles = 0;

            _wnd.Closed += (o,a) =>
            {
                wndClosed = true;
            };

            return Dispatcher.CurrentDispatcher.WaitOne(() => { return wndClosed; });

            //int timeout = Environment.TickCount + (int)UITestHelper.GetTimeout().TotalMilliseconds;

            //Trace.WriteLine(Environment.TickCount);
            //Trace.WriteLine((int)UITestHelper.GetTimeout().TotalMilliseconds);
            //Trace.WriteLine(timeout);

            //while (!wndClosed && Environment.TickCount < timeout)
            //{
            //    Dispatcher.CurrentDispatcher.Invoke((Action)delegate()
            //    {
            //        ++cycles;
            //    }, DispatcherPriority.Background);
            //}
            //return wndClosed;
        }

        /// <summary>
        /// Flushes/waits until the Dispatcher's message queue has run empty.
        /// </summary>
        public static void FlushDispatcher()
        {
            Dispatcher.CurrentDispatcher.Invoke((Action)delegate()
            {
            }, DispatcherPriority.ApplicationIdle);
        }

        /// <summary>
        /// Fully initializes the UIHost, so that <see cref="Window"/> can already be accessed.
        /// </summary>
        public void FinalizeInitialization()
        {
            checkAndCreateWindow();
        }

        #region Internals
        private void checkAndCreateWindow()
        {
            if (_wnd != null && _wnd.IsLoaded)
            {
                _wnd.Close();
                throw new InvalidOperationException("Close previous window before showing a new one!");
            }

            // ============================================================
            // NavigationWindow & Page
            // ============================================================
            if (this.Page != null)
            {
                if (_wnd == null)
                {
                    _wnd = new System.Windows.Navigation.NavigationWindow();
                }
                _wnd.Content = this.Page;
                return;
            }

            // ============================================================
            // Window & Control
            // ============================================================
            if (this.Control is Window)
            {
                _wnd = (Window)this.Control;
            }
            else
            {
                if (_wnd == null)
                {
                    _wnd = new WindowEx();

                    if (WindowLoaded != null)
                    {
                        WindowLoaded();
                    }
                }

                //CultureInfo ci = CultureInfo.GetCultureInfo("de-DE");
                //Thread thd = Dispatcher.CurrentDispatcher.Thread;
                //thd.CurrentCulture = ci;
                //thd.CurrentUICulture = ci;

                _wnd.Content = this.Control;
                this.Control.HorizontalAlignment = HorizontalAlignment.Stretch;
                this.Control.VerticalAlignment = VerticalAlignment.Stretch;
            }
        }

        class WindowEx : Window
        {
            public WindowEx()
            {
                Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
            }
        } 
        #endregion
    }
}
